﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ZyGame.Execute;
using UnityEngine;
using ZyGame.Resource;

namespace ZyGame.Transport
{
    public enum DownloadState : byte
    {
        None,
        Process,
        Completed,
        Failure
    }

    public class MultiFileDownload : Runnable<List<DownloadContext>>, IDisposable
    {
        private const int MAX_DOWNLOAD_COUNT = 10;
        private DownloadState downloadState;
        private Action<float> progres_callback;
        private List<DownloadContext> downloading = new List<DownloadContext>();
        private Queue<DownloadContext> waitingList = new Queue<DownloadContext>();
        private List<DownloadContext> download_completion = new List<DownloadContext>();

        public static MultiFileDownload Create(params PackageInfo[] files)
        {
            string name = $"[MULTI DOWNLOAD]" + string.Join("", files.Select(x => x.name));
            if (Client.Running.TryGetOrCreate(name, out MultiFileDownload download))
            {
                return download;
            }

            foreach (var item in files)
            {
                download.waitingList.Enqueue(new DownloadContext(item));
            }

            download.Execute(download.OnStartDownload);
            return download;
        }

        public Runnable<List<DownloadContext>> ListenerProgres(Action<float> callback)
        {
            progres_callback = callback;
            return this;
        }

        private IEnumerator OnStartDownload()
        {
            Client.Console.WriteLine("runing download executed");
            OnStartNext();
            yield return new WaitUntil(HasCompletetion);
        }

        private bool HasCompletetion()
        {
            OnUpdateDownloadProgress();
            return downloadState == DownloadState.Completed;
        }

        private void OnUpdateDownloadProgress()
        {
            if (downloading.Count == 0 && waitingList.Count == 0)
            {
                return;
            }

            float temp = downloading.Sum(x => x.progress) + download_completion.Count;
            progres_callback(temp / (download_completion.Count + downloading.Count + waitingList.Count));
        }

        private void OnStartNext()
        {
            if (waitingList.Count == 0 && downloading.Count == 0)
            {
                if (progres_callback != null)
                {
                    progres_callback(1f);
                    progres_callback = null;
                }

                SetResult(download_completion.Where(x => x.state == DownloadState.Completed).ToList());
                downloadState = DownloadState.Completed;
                return;
            }

            if (downloading.Count > MAX_DOWNLOAD_COUNT || waitingList.Count == 0)
            {
                return;
            }

            Client.Tools.StartCoroutine(DownloadFile(waitingList.Dequeue()));
            OnStartNext();
        }

        private IEnumerator DownloadFile(DownloadContext unit)
        {
            downloading.Add(unit);
            yield return unit.Start();
            downloading.Remove(unit);
            download_completion.Add(unit);
            OnStartNext();
        }

        public override bool EnsureExecutedSuccessful()
        {
            return download_completion.Find(x => x.state is not DownloadState.Completed) == null;
        }
    }
}